package com.itgaohe.utils;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.*;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.net.URL;
import java.util.Arrays;
import java.util.Date;
import java.util.UUID;

/**
 * 阿里云OSS文件上传工具类
 * 支持图片、视频、音频等文件上传
 */
@Component
public class AliyunOssUtil {

    private String endpoint = "oss-cn-beijing.aliyuncs.com" ;

    private String accessKeyId = "LTAI5tBkNDWmGxEVbJ3WsUsy";

    private String accessKeySecret = "5ZJHtX1aBbEf1ZQNbQM2cEvZH6zzSy" ;

    private String bucketName = "yzhh";

    private OSS ossClient;


    public String getEndpoint() {
        return endpoint;
    }

    public void setEndpoint(String endpoint) {
        this.endpoint = endpoint;
    }

    public String getAccessKeyId() {
        return accessKeyId;
    }

    public void setAccessKeyId(String accessKeyId) {
        this.accessKeyId = accessKeyId;
    }

    public String getAccessKeySecret() {
        return accessKeySecret;
    }

    public void setAccessKeySecret(String accessKeySecret) {
        this.accessKeySecret = accessKeySecret;
    }

    public String getBucketName() {
        return bucketName;
    }

    public void setBucketName(String bucketName) {
        this.bucketName = bucketName;
    }

    public OSS getOssClient() {
        return ossClient;
    }

    public void setOssClient(OSS ossClient) {
        this.ossClient = ossClient;
    }

    // 允许的文件类型
    private static final String[] IMAGE_TYPES = {".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp"};
    private static final String[] VIDEO_TYPES = {".mp4", ".avi", ".mov", ".wmv", ".flv", ".mkv"};
    private static final String[] AUDIO_TYPES = {".mp3", ".wav", ".aac", ".wma", ".flac"};

    //注解的方法在Bean初始化时执行，用于创建OSS客户端
    @PostConstruct
    public void init() {
        this.ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
    }
    //注解的方法在Bean销毁时执行，用于关闭OSS客户端
    @PreDestroy
    public void destroy() {
        if (ossClient != null) {
            ossClient.shutdown();
        }
    }

    /**
     * 上传文件到指定文件夹
     *
     * @param file     文件
     * @param folder   文件夹路径 (如: images, videos, audios)
     * @return 文件访问URL
     */
    public String uploadFile(MultipartFile file, String folder) {
        try {
            // 验证文件类型
            String originalFilename = file.getOriginalFilename();
            String fileType = getFileType(originalFilename);

            // 生成唯一文件名
            String fileName = generateFileName(originalFilename, fileType);
            String objectName = folder + "/" + fileName;

            // 上传文件
            ossClient.putObject(bucketName, objectName, file.getInputStream());

            // 返回文件访问URL
            return generateFileUrl(objectName);
        } catch (IOException e) {
            throw new RuntimeException("文件上传失败", e);
        }
    }

    /**
     * 上传图片文件 (自动分类到images文件夹)
     *
     * @param imageFile 图片文件
     * @return 图片访问URL
     */
    public String uploadImage(MultipartFile imageFile) {
        // 验证是否为图片
        if (!isImage(imageFile.getOriginalFilename())) {
            throw new IllegalArgumentException("文件类型不是图片");
        }
        return uploadFile(imageFile, "images");
    }

    /**
     * 上传视频文件 (自动分类到videos文件夹)
     *
     * @param videoFile 视频文件
     * @return 视频访问URL
     */
    public String uploadVideo(MultipartFile videoFile) {
        // 验证是否为视频
        if (!isVideo(videoFile.getOriginalFilename())) {
            throw new IllegalArgumentException("文件类型不是视频");
        }
        return uploadFile(videoFile, "videos");
    }

    /**
     * 上传音频文件 (自动分类到audios文件夹)
     *
     * @param audioFile 音频文件
     * @return 音频访问URL
     */
    public String uploadAudio(MultipartFile audioFile) {
        // 验证是否为音频
        if (!isAudio(audioFile.getOriginalFilename())) {
            throw new IllegalArgumentException("文件类型不是音频");
        }
        return uploadFile(audioFile, "audios");
    }



    /**
     * 上传本地文件到OSS
     *
     * @param localFilePath 本地文件路径
     * @param folder        OSS文件夹路径
     * @return 文件访问URL
     */
    //TODO 考虑是否删除该功能
    public String uploadLocalFile(String localFilePath, String folder) {
        File file = new File(localFilePath);
        if (!file.exists()) {
            throw new IllegalArgumentException("本地文件不存在");
        }

        String fileName = generateFileName(file.getName(), getFileType(file.getName()));
        String objectName = folder + "/" + fileName;

        try (InputStream inputStream = new FileInputStream(file)) {
            ossClient.putObject(bucketName, objectName, inputStream);
            return generateFileUrl(objectName);
        } catch (IOException e) {
            throw new RuntimeException("本地文件上传失败", e);
        }
    }

    /**
     * 大文件分片上传 (适合大文件上传)
     *
     * @param file   文件
     * @param folder 文件夹路径
     * @return 文件访问URL
     */
    public String uploadBigFile(MultipartFile file, String folder) {
        try {
            String originalFilename = file.getOriginalFilename();
            String fileName = generateFileName(originalFilename, getFileType(originalFilename));
            String objectName = folder + "/" + fileName;

            // 创建分片上传请求
            InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, objectName);
            InitiateMultipartUploadResult result = ossClient.initiateMultipartUpload(request);
            String uploadId = result.getUploadId();

            // 计算分片数 (每片5MB)
            final long partSize = 5 * 1024 * 1024;
            long fileLength = file.getSize();
            int partCount = (int) (fileLength / partSize);
            if (fileLength % partSize != 0) {
                partCount++;
            }

            // 分片上传
            UploadPartRequest uploadPartRequest;
            PartETag[] partETags = new PartETag[partCount];
            for (int i = 0; i < partCount; i++) {
                long startPos = i * partSize;
                long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
                InputStream inputStream = file.getInputStream();
                inputStream.skip(startPos);

                uploadPartRequest = new UploadPartRequest();
                uploadPartRequest.setBucketName(bucketName);
                uploadPartRequest.setKey(objectName);
                uploadPartRequest.setUploadId(uploadId);
                uploadPartRequest.setInputStream(inputStream);
                uploadPartRequest.setPartSize(curPartSize);
                uploadPartRequest.setPartNumber(i + 1);

                UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
                partETags[i] = uploadPartResult.getPartETag();
            }

            // 完成分片上传
            CompleteMultipartUploadRequest completeRequest =
                    new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, Arrays.asList(partETags));
            ossClient.completeMultipartUpload(completeRequest);

            return generateFileUrl(objectName);
        } catch (Exception e) {
            throw new RuntimeException("大文件上传失败", e);
        }
    }

    /**
     * 生成文件访问URL
     *
     * @param objectName OSS文件路径
     * @return 文件访问URL
     */
    public String generateFileUrl(String objectName) {
        return "https://" + bucketName + "." + endpoint + "/" + objectName;
    }

    /**
     * 生成临时访问URL (带过期时间)
     *
     * @param objectName OSS文件路径
     * @param expireTime 过期时间(秒)
     * @return 临时访问URL
     */
    public String generateTempUrl(String objectName, long expireTime) {
        Date expiration = new Date(System.currentTimeMillis() + expireTime * 1000);
        URL url = ossClient.generatePresignedUrl(bucketName, objectName, expiration);
        return url.toString();
    }

    /**
     * 删除OSS文件
     *
     * @param objectName OSS文件路径
     */
    public void deleteFile(String objectName) {
        ossClient.deleteObject(bucketName, objectName);
    }

    /**
     * 生成唯一文件名
     *
     * @param originalFilename 原始文件名
     * @param fileType         文件后缀
     * @return 唯一文件名
     */
    private String generateFileName(String originalFilename, String fileType) {
        return UUID.randomUUID().toString() + fileType;
    }

    /**
     * 获取文件后缀
     *
     * @param filename 文件名
     * @return 文件后缀
     */
    private String getFileType(String filename) {
        if (filename == null || filename.lastIndexOf(".") == -1) {
            throw new IllegalArgumentException("文件名不合法");
        }
        return filename.substring(filename.lastIndexOf(".")).toLowerCase();
    }

    /**
     * 判断是否为图片
     *
     * @param filename 文件名
     * @return 是否为图片
     */
    public boolean isImage(String filename) {
        String fileType = getFileType(filename);
        for (String type : IMAGE_TYPES) {
            if (type.equalsIgnoreCase(fileType)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断是否为视频
     *
     * @param filename 文件名
     * @return 是否为视频
     */
    public boolean isVideo(String filename) {
        String fileType = getFileType(filename);
        for (String type : VIDEO_TYPES) {
            if (type.equalsIgnoreCase(fileType)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断是否为音频
     *
     * @param filename 文件名
     * @return 是否为音频
     */
    public boolean isAudio(String filename) {
        String fileType = getFileType(filename);
        for (String type : AUDIO_TYPES) {
            if (type.equalsIgnoreCase(fileType)) {
                return true;
            }
        }
        return false;
    }
}
